#include "mibs.h"

#include "MIBS.h"
#include "common.h"
#include"set_error_insert.h"
#include <iostream>
#include <stdlib.h>

using namespace std ;

MIBS::MIBS( const int BlockSize ,
                   const int Round ,
                   const int SboxSize ,
                   key_t InitialKey ,
                   plaintext_t Plaintext ,
                   ciphertext_t& Ciphertext ) : BaseCipher( BlockSize , Round , SboxSize , InitialKey , Plaintext )
{
}

MIBS::~MIBS(void)
{
}

void MIBS::Encryption(ciphertext_t& ciphertextvector)
{
    int blocksize = 0 ;
    int round = 0 ;
    int sboxsize = 0 ;
    key_t initial_key ;
    plaintext_t plaintext ;
    get_CipherInfo( blocksize , round , sboxsize , initial_key , plaintext ) ;
/*	nibble use_key[MIBS_ROUND][MIBS_KEY_NIBBLE] = { 0 } ;*/
    KeySchedule( initial_key ) ;
    bit_to_nibble( plaintext , innerstate , MIBS_SBOX_NUM ) ;
    for ( int i = 0 ; i < round ; ++i )
    {
        error_insert( innerstate , MIBS_INNER_NUM , i ) ;
        OneRound( i ) ;
    }
    nibble temp[MIBS_KEY_NIBBLE] = { 0 } ;
    for ( int i = 0 ; i < MIBS_KEY_NIBBLE ; ++i )
    {
        temp[i] = innerstate[i] ;
        innerstate[i] = innerstate[i+MIBS_KEY_NIBBLE] ;
        innerstate[i+MIBS_KEY_NIBBLE] = temp[i] ;
    }
    nibble_to_bit( innerstate , MIBS_SBOX_NUM , Ciphertext ) ;
    for(unsigned int i = 0; i < Ciphertext.size(); ++i)
    {
        bit ciphertemp = Ciphertext.at(i);
        ciphertextvector.push_back(ciphertemp);
    }
}

void MIBS::Decryption()
{

}

void MIBS::mibs_keyxor( nibble in_key[] , nibble roundkey[] , const int array_num , nibble out_key[] )
{
    int i = 0 ;
    for ( i = 0 ; i < array_num ; ++i )
    {
        out_key[i] = in_key[i] ^ roundkey[i] ;
    }
}
void MIBS::mibs_sbox( nibble in_sbox[] , const int array_num , nibble out_sbox[] )
{
    for ( int i = 0 ; i < array_num ;  ++i )
    {
        out_sbox[i] = MIBS_SBOX[in_sbox[i]] ;
    }
}

void MIBS::mibs_p( nibble in_p[] , const int array_num , nibble out_p[] )
{
    int i = 0 ;
    for ( i = 0 ; i < array_num ; ++i )
    {
        out_p[MIBS_P[i]] = in_p[i] ;
    }
}

void MIBS::mibs_mix( nibble in_mix[] , const int array_num , nibble out_mix[] )
{
    out_mix[0] = in_mix[1] ^ in_mix[2] ^ in_mix[3] ^ in_mix[4] ^ in_mix[5] ^ in_mix[6] ;
    out_mix[1] = in_mix[0] ^ in_mix[2] ^ in_mix[3] ^ in_mix[5] ^ in_mix[6] ^ in_mix[7] ;
    out_mix[2] = in_mix[0] ^ in_mix[1] ^ in_mix[3] ^ in_mix[4] ^ in_mix[6] ^ in_mix[7] ;
    out_mix[3] = in_mix[0] ^ in_mix[1] ^ in_mix[2] ^ in_mix[4] ^ in_mix[5] ^ in_mix[7] ;
    out_mix[4] = in_mix[0] ^ in_mix[1] ^ in_mix[3] ^ in_mix[4] ^ in_mix[5] ;
    out_mix[5] = in_mix[0] ^ in_mix[1] ^ in_mix[2] ^ in_mix[5] ^ in_mix[6] ;
    out_mix[6] = in_mix[1] ^ in_mix[2] ^ in_mix[3] ^ in_mix[6] ^ in_mix[7] ;
    out_mix[7] = in_mix[0] ^ in_mix[2] ^ in_mix[3] ^ in_mix[4] ^ in_mix[7] ;
}

void MIBS::KeySchedule( key_t InitialKey )
{
//    FILE* keyfile = fopen( "f:\\mibs-adfa\\key\\roundkey.txt" , "w+" ) ;
    word userkey[MIBS_WORD_NUM] = { 0 } ;
    bit_to_word( InitialKey , userkey , MIBS_WORD_NUM ) ;
    word keystate[MIBS_WORD_NUM] = { 0 } ;
    for ( int i = 0 ; i < MIBS_WORD_NUM ; ++i )
    {
        keystate[i] = userkey[i] ;
    }
    for ( int i = 0 ; i < MIBS_ROUND ; ++i )
    {
        userkey[1] = keystate[1] ;
        keystate[1] = (keystate[1]>>15) + (keystate[0]<<17) ;
        keystate[0] = (keystate[0]>>15) + (userkey[1]<<17) ;
        keystate[1] = (MIBS_SBOX[(keystate[1]>>28)&0x0f]<<28) + (keystate[1]&0xfffffff) ;
        keystate[0] = keystate[0] ^ ((i+1)*0x800) ;
        for ( int j = 0 ; j < MIBS_KEY_NIBBLE ; ++j )
        {
            RoundKey[i][7-j] = (keystate[1]>>(4*j))&0x0f ;
            for ( int k = 0 ; k < 4 ; ++k )
            {
                bit temp = 0 ;
                temp = (RoundKey[i][7-j] >> k) & 0x01 ;
 //               fprintf( keyfile , "k[%d]+%x=0\n" , (32*(i+1)-4*j-k) , temp ) ;
            }
        }
//        printf( "%x \n" , keystate[1] ) ;
    }
}

void MIBS::OneRound( const int round )
{
    nibble leftpart[MIBS_KEY_NIBBLE] = { 0 } ;
    nibble rightpart[MIBS_KEY_NIBBLE] = { 0 } ;
    nibble temp[MIBS_KEY_NIBBLE] = { 0 } ;
    int i = 0 ;
    for ( i = 0 ; i < MIBS_KEY_NIBBLE ; ++i )
    {
        leftpart[i] = innerstate[i] ;
        rightpart[i] = innerstate[i+8] ;
    }
    mibs_keyxor( leftpart , RoundKey[round] , MIBS_KEY_NIBBLE , leftpart ) ;
    mibs_sbox( leftpart , MIBS_KEY_NIBBLE , leftpart ) ;
 //   printf( "\nSbox %d  " , round ) ;
//    for ( i = 0 ; i < MIBS_KEY_NIBBLE ; ++i )
//    {
//        printf( "%x" , leftpart[i] ) ;
//    }

    mibs_mix( leftpart , MIBS_KEY_NIBBLE , temp ) ;
    mibs_p( temp , MIBS_KEY_NIBBLE , leftpart ) ;
    for ( i = 0 ; i < MIBS_KEY_NIBBLE ; ++i )
    {
        leftpart[i] = innerstate[i+8] ^ leftpart[i] ;
        innerstate[i+8] = innerstate[i] ;
        innerstate[i] = leftpart[i] ;
    }
}

void MIBS::InvOneRound()
{

}

void MIBS::print(  )
{
    int b = 0 ;
    int c = 0 ;
    int a = 0 ;
    key_t d ;
    plaintext_t e ;
    get_CipherInfo( b , c , a , d , e ) ;

//    for ( key_iter = 0 ; key_iter < key_size ; ++key_iter )
//    {
//        printf( "%d\n" , d[key_iter] ) ;
//    }
    cout << b << c << endl ;
}




